package com.hmall.gateway.routers;

import cn.hutool.json.JSONUtil;
import com.alibaba.cloud.nacos.NacosConfigManager;
import com.alibaba.nacos.api.config.listener.Listener;
import com.alibaba.nacos.api.exception.NacosException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionWriter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.annotation.PostConstruct;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Executor;

@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicRouteLoader {
    private final NacosConfigManager nacosConfigManager;

    private final RouteDefinitionWriter writer;

    private final String dataId = "gateway-routes.json";

    private final String group = "DEFAULT_GROUP";

    private final Set<String> routeIds=  new HashSet<>();//用来保存已经加载的路由规则ID,防止重复加载

    @PostConstruct//这个注解的意思是在这个Bean初始化之后执行该方法
    public void initRouteConfigListener() throws NacosException {
        //1.项目启动先从 Nacos 上拉取配置信息,并且监听配置变化
        String configInfo = nacosConfigManager.getConfigService().
                getConfigAndSignListener(dataId, group, 5000, new Listener() {
                    @Override
                    public Executor getExecutor() {
                        return null;
                    }

                    @Override
                    public void receiveConfigInfo(String configInfo) {
                //2.配置信息发生变化时,需要重新加载（更新）路由规则
                        updateConfigInfo(configInfo);
                    }
                });
        //3.第一次加载配置信息，也需要加更新到路由规则中
        updateConfigInfo(configInfo);
    }

    public void updateConfigInfo(String configInfo)  {
        log.debug("监听到路由配置信息: {}", configInfo);
        //1.解析配置信息，转换成RouteDefinition对象
        List<RouteDefinition> routeDefinitions = JSONUtil.toList(configInfo, RouteDefinition.class);
        //2.删除旧的路由表
        for (String routeId : routeIds) {
            writer.delete(Mono.just(routeId)).subscribe();
        }
       routeIds.clear();
        //3.更新路由表
        for (RouteDefinition routeDefinition : routeDefinitions) {
            //3.1更新路由表，保存路由规则到内存中
            writer.save(Mono.just(routeDefinition)).subscribe();//响应式编程的处理方式
            //3.2保存路由规则ID
            routeIds.add(routeDefinition.getId());
        }
    }
}

